package ee.logsimulator.ui;

import ee.logsimulator.Model;
import ee.logsimulator.data.ActorEvents;
import ee.logsimulator.data.Clazz;
import ee.logsimulator.data.Event;
import ee.logsimulator.data.Spell;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class Player extends AbstractPlayer {
  private static final long serialVersionUID = 1L;

  public Player(Model model) {
    super(model);
  }

  @Override
  public void paint(Graphics graphics) {
    if (scroll != null) {
      int h = scroll.getHeight();
      if (scroll.getHeight() < getHeight()) {
        h -= 24;
      }
      setSize(getWidth(), h);
    }

    if (i == null || i.getWidth(this) != getWidth() || i.getHeight(this) != getHeight()) {
      i = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
    }
    Graphics g = i.getGraphics();

    if (!pixelsCalculated) {
      fillEventPixels(g);
    }

    time = slider.getValue();
    long until = timeStart + time;
    int bottom = getHeight();
    int middle = getWidth() / 2;
    int startY = bottom - 10 - ACTOR_HEIGHT;
    long drawFrom = until - Math.round(startY / MILLIS_PER_PIXEL);
    // background
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, getWidth(), getHeight());

    for (ActorEvents ae : actorEvents) {
      // boxes
      int pos = ae.getPosition();
      g.setColor(Color.WHITE);
      int startX = middle + (pos * (ACTOR_WIDTH + 10) + 5);
      g.drawRect(startX, startY, ACTOR_WIDTH, ACTOR_HEIGHT);
      g.drawRect(startX, 0, ACTOR_WIDTH, startY);

      // name
      Font font = g.getFont();
      font = font.deriveFont(24f);
      font = font.deriveFont(Font.BOLD);
      g.setFont(font);
      g.setColor(Clazz.fromName(ae.getActor().getActorClass()).getColor());
      FontMetrics metrics = g.getFontMetrics();
      String name = ae.getActor().getName();
      int nameW = metrics.stringWidth(name);
      if (nameW > ACTOR_WIDTH - 20) {
        name = shoertenSpellTo(name, metrics, ACTOR_WIDTH - 20);
        nameW = metrics.stringWidth(name);
      }
      int nameHeight = metrics.getHeight();
      g.drawString(name, startX + (ACTOR_WIDTH / 2 - nameW / 2), startY + nameHeight);

      if (time == 0) {
        continue;
      }
      // calculate and fill lists
      Map<Spell, Integer> damageDone = new HashMap<Spell, Integer>();
      List<Event> toDraw = new ArrayList<Event>();
      readEvents(until, drawFrom, ae.getEvents(), damageDone, toDraw);

      // abilities
      font = font.deriveFont(14f);
      font = font.deriveFont(Font.PLAIN);
      g.setFont(font);
      metrics = g.getFontMetrics();
      g.setColor(Color.WHITE);
      List<Entry<Spell, Integer>> spells = sortEntrySet(damageDone);
      int nr = 1;
      int spellStartX = startX + 10;
      for (Entry<Spell, Integer> e : spells) {
        int textStartY = startY + nameHeight + nr * metrics.getHeight();
        if (textStartY > bottom - 10) {
          break;
        }
        String amount = abrevateNumber(e.getValue(), 1, 3, 1);
        int len = metrics.stringWidth(amount);
        String spellName = e.getKey().getName();
        int amountStartX = startX + ACTOR_WIDTH - 10 - len;
        if (spellStartX + metrics.stringWidth(spellName) > amountStartX) {
          spellName = shoertenSpellTo(spellName, metrics, amountStartX - spellStartX);
        }
        g.drawString(spellName, spellStartX, textStartY);
        g.drawString(amount, amountStartX, textStartY);
        g.drawRect(startX + 5, textStartY - metrics.getHeight() + 4, ACTOR_WIDTH - 10, metrics.getHeight());
        nr++;
      }

      // timeline
      Font boldFont = setTimelineBoldFont(g);
      FontMetrics boldMetrics = g.getFontMetrics();
      Font extra = setTimelineExtraFont(g);
      FontMetrics extraMetrics = g.getFontMetrics();
      font = setTimelineFont(g);
      int firstPixelTime = (int) Math.round(time * MILLIS_PER_PIXEL);
      for (Event e : toDraw) {
        g.setFont(font);
        g.setColor(model.getTartetColor(e.getTarget()));
        int pixelTime = firstPixelTime - e.getPixelTime();
        FontMetrics m = e.getAmount().isCrit() ? boldMetrics : metrics;
        if (startY - pixelTime - m.getHeight() < 0) {
          continue;
        }
        if (startY - pixelTime > startY) {
          break;
        }
        String spellName = e.getSpell().getName();
        String amount = abrevateNumber(e.getAmount().getAmount(), 1, 3, 1);
        int len = m.stringWidth(amount);
        String amountExtra = getAmountExtra(e.getAmount());
        if (amountExtra != null) {
          len += extraMetrics.stringWidth(amountExtra);
        }
        int amountStartX = startX + ACTOR_WIDTH - 10 - len;
        if (spellStartX + metrics.stringWidth(spellName) > amountStartX) {
          spellName = shoertenSpellTo(spellName, metrics, amountStartX - spellStartX);
        }
        g.drawString(spellName, spellStartX, startY - pixelTime);
        if (e.getAmount().isCrit()) {
          g.setColor(Color.YELLOW);
          g.setFont(boldFont);
        } else {
          g.setColor(Color.WHITE);
        }
        g.drawString(amount, amountStartX, startY - pixelTime);
        if (amountExtra != null) {
          g.setColor(Color.WHITE);
          g.setFont(extra);
          int nextStartX = amountStartX + m.stringWidth(amount);
          g.drawString(" (", nextStartX, startY - pixelTime);
          nextStartX += extraMetrics.stringWidth(" (");
          boolean added = false;
          if (e.getAmount().isOverkill()) {
            added = true;
            g.setColor(Color.RED);
            String str = "" + abrevateNumber(e.getAmount().getOverkill(), 1, 3, 0);
            g.drawString(str, nextStartX, startY - pixelTime);
            nextStartX += extraMetrics.stringWidth(str);
          }
          if (e.getAmount().isOverheal()) {
            g.setColor(Color.RED);
            String str = (added ? " " : "") + abrevateNumber(e.getAmount().getOverheal(), 1, 3, 0);
            g.drawString(str, nextStartX, startY - pixelTime);
            nextStartX += extraMetrics.stringWidth(str);
            added = true;
          }
          if (e.getAmount().isBlock()) {
            g.setColor(Color.ORANGE);
            String str = (added ? " " : "") + abrevateNumber(e.getAmount().getBlocked(), 1, 3, 0);
            g.drawString(str, nextStartX, startY - pixelTime);
            nextStartX += extraMetrics.stringWidth(str);
            added = true;
          }
          if (e.getAmount().isAbsorb()) {
            g.setColor(Color.GREEN);
            String str = (added ? " " : "") + abrevateNumber(e.getAmount().getAbsorbed(), 1, 3, 0);
            g.drawString(str, nextStartX, startY - pixelTime);
            nextStartX += extraMetrics.stringWidth(str);
            added = true;
          }
          if (e.getAmount().isResist()) {
            g.setColor(Color.CYAN);
            String str = (added ? " " : "") + abrevateNumber(e.getAmount().getResisted(), 1, 3, 0);
            g.drawString(str, nextStartX, startY - pixelTime);
            nextStartX += extraMetrics.stringWidth(str);
            added = true;
          }
          g.setColor(Color.WHITE);
          g.drawString(")", nextStartX, startY - pixelTime);
        }
      }
    }

    graphics.drawImage(i, 0, 0, this);
  }
}
